package org.ala.tiktools.sharddb.mybatis.router;

import org.ala.tiktools.mybatis.spring.TikToolsMyBatisSpringContext;
import org.ala.tiktools.sharddb.Namespace;
import org.ala.tiktools.sharddb.exception.ShardDBRuntimeException;
import org.ala.tiktools.tools.CachingKVTools;
import org.ala.tiktools.tools.kv.KV;
import org.ala.tiktools.tools.kv.Maps;
import org.apache.ibatis.annotations.Param;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 路由执行器
 *
 * @author ala
 * @date 2025-02-19 11:35
 */
public class ShardRouterHandler {


    /**
     *  执行分库分表路由
     */
    public Map<ShardInfo, Object[]> route(IShardRouter router, Object proxy, Method method, Object[] args) {
        Map<ShardInfo, Object[]> res = new HashMap<>();

        //  分库分表参数下标（要被切分的参数）
        int shardRouteIdx = shardRouterIndex.get(method);

        //  执行分库分表
        Object arg = args[shardRouteIdx];
        Map<ShardInfo, Object> shards = router.route(arg);
        //  回填参数
        shards.forEach((k,v) -> {
            res.put(k, copyWithoutIdx(shardRouteIdx, v, args));
        });
        return res;
    }
    /**
     *  执行分库分表路由
     */
    public Map<ShardInfo, Object[]> route(Object proxy, Method method, Object[] args) {
        //  分库分表参数下标（要被切分的参数）
        IShardRouter router = shardRouterMapper.get(method);
        if (router == null) {throw new ShardDBRuntimeException(String.format("方法参数中无法找到分库分表路由 method:%s class:%s", method.getName(), method.getDeclaringClass().getSimpleName()));}

        return route(router, proxy, method, args);
    }


    /**
     *  覆盖第idx的下标，其余值不变
     */
    protected Object[] copyWithoutIdx(int i, Object tag, Object[] src) {
        Object[] copy = new Object[src.length];
        for (int j= 0; j < copy.length; j++) {
            if (j == i) {copy[j] = tag;}
            else {copy[j] = src[j];}
        }
        return copy;
    }


    /**
     * 方法的@Param("shardSuffix")注解参数下标
     */
    protected CachingKVTools<Method, Integer> paramShardSuffixIndex = new CachingKVTools<>(-1, m -> {
        for (int i = 0; i < m.getParameters().length; i++) {
            Annotation[] annos = m.getParameters()[i].getAnnotations();
            if (annos == null) {continue;}

            for (Annotation anno : annos) {
                if ((anno instanceof Param) && "shardSuffix".equals(((Param) anno).value())) {
                    return  i;
                }
            }
        }
        return null;
    });


    /**
     *  方法参数中ShardRouter注解的下标
     */
    protected CachingKVTools<Method, Integer> shardRouterIndex = new CachingKVTools<>(-1, m -> {
        for (int i = 0; i < m.getParameters().length; i++) {
            Annotation[] annos = m.getParameters()[i].getAnnotations();
            if (annos == null) {continue;}

            for (Annotation anno : annos) {
                if (anno instanceof ShardRouter) {
                    return i;
                }
            }
        }
        return null;
    });
    protected IShardRouter NULL = new IShardRouter(){
        public Map route(Object o) {
            return Collections.emptyMap();
        }
    };
    /**
     *  方法与ShardRouter绑定
     */
    protected CachingKVTools<Method, IShardRouter> shardRouterMapper = new CachingKVTools<>(NULL, m -> {
        Integer idx = shardRouterIndex.get(m);
        if (idx == null || idx < 0) {return null;}

        ShardRouter anno = null;
        Annotation[] annos = m.getParameters()[idx].getAnnotations();
        for (Annotation a : annos) {
            if (a instanceof ShardRouter) {
                anno = (ShardRouter) a;
                break;
            }
        }

        IShardRouter router = null;
        try {
            router = TikToolsMyBatisSpringContext.getBeanOrNew(anno.router());
        } catch (InstantiationException | IllegalAccessException e) {
            throw new ShardDBRuntimeException(String.format("无法找到类型为(%s)的Beaen，也无法实例化", anno.router().getName()), e);
        }
        return router;
    });
}
